namespace HtmlLogProviderCS
{
    using System;
    using Microsoft.SqlServer.Dts.Runtime;
    using System.IO;
    using System.Web.UI;
    using System.Web;

    // The LogProviderType property is required but not used.
    //  The custom log provider will not appear in the list
    //  if a LogProviderType is not provided.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable"), DtsLogProvider(DisplayName = "Custom log provider for HTML files (CS)",
    Description = "Writes log entries for events to an HTML file",
    LogProviderType = "Custom")]
    public class HtmlLogProviderCS : LogProviderBase
    {
        #region Variables and constants

        // Constants.
        private const string SUBCOMPONENT = "HtmlLogProviderCS";
        private const string PACKAGE_END_EVENT = "PackageEnd";

        // Variables.
        private Microsoft.SqlServer.Dts.Runtime.Connections _connections;
        private Microsoft.SqlServer.Dts.Runtime.IDTSInfoEvents _events;
        private string _configString;
        private string _logFile;
        private HtmlLogWriterCS _htmlLogWriter;
        private MemoryStream _logStream;
        private StreamWriter _logStreamWriter;

        // Status flags.
        private bool _fireEventsAgain;
        private bool _loggingAlreadyStarted;
        private bool _packageHasEnded;

        #endregion

        #region Initialize and Validate

        public override void InitializeLogProvider(Microsoft.SqlServer.Dts.Runtime.Connections connections, Microsoft.SqlServer.Dts.Runtime.IDTSInfoEvents events, Microsoft.SqlServer.Dts.Runtime.ObjectReferenceTracker refTracker)
        {
            // Cache object references for later use.
            this._connections = connections;
            this._events = events;

            this._fireEventsAgain = true;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public override Microsoft.SqlServer.Dts.Runtime.DTSExecResult Validate(Microsoft.SqlServer.Dts.Runtime.IDTSInfoEvents events)
        {
            if (events == null)
            {
                throw new ArgumentNullException("events");
            }

            string fileExtension;

            this._configString = this.ConfigString;

            // Configuration string should contain the name of the selected File connection manager.
            // Validate function needs to validate:
            // 1. Has a config string been provided?
            // 2. Does the specified connection manager exist?
            // 3. Can the connection string be retrieved successfully?
            // 4. Is the connection string empty?
            // 5. Is the specified file an HTML file with an appropriate extension?
            // Do not check for file existence, because user may have
            //  specified to create a new file at run time.

            if (this._configString.Length == 0)
            {
                // Validation test 1: Has a config string been provided?
                events.FireError(0, SUBCOMPONENT, "The log provider did not receive connection information.", String.Empty, 0);
                return DTSExecResult.Failure;
            }
            else if (!this._connections.Contains(this._configString))
            {
                events.FireError(0, SUBCOMPONENT, "The specified connection manager cannot be found.", String.Empty, 0);
                return DTSExecResult.Failure;
            }
            else
            {
                // Validation test 3: Can the connection string be retrieved successfully?
                try
                {
                    this._logFile = (String)this._connections[this._configString].AcquireConnection(null);
                }
                catch
                {
                    events.FireError(0, SUBCOMPONENT, "The specified connection manager did not return a filename string.", String.Empty, 0);
                    return DTSExecResult.Failure;
                }
            }

            if (String.IsNullOrEmpty(this._logFile))
            {
                // Validation test 4: Is the connection string empty?
                events.FireError(0, SUBCOMPONENT, "The specified connection manager does not contain a filename.", String.Empty, 0);
                return DTSExecResult.Failure;
            }
            else
            {
                // Validation test 5: Is the specified file an HTML file with an appropriate extension?
                fileExtension = this._logFile.Substring(this._logFile.LastIndexOf(".") + 1);
                if (!(fileExtension == "htm" || fileExtension == "html"))
                {
                    events.FireError(0, SUBCOMPONENT, "The filename specified by the connection manager does not have an .htm or .html extension.", String.Empty, 0);
                    return DTSExecResult.Failure;
                }
            }

            return DTSExecResult.Success;
        }

        #endregion

        #region OpenLog and CloseLog

        public override void OpenLog()
        {
            string subComponentInfo = SUBCOMPONENT + "-OpenLog ";

            if (this._fireEventsAgain)
            {
                this._events.FireInformation(0, subComponentInfo, "Opening HTML log.", String.Empty, 0, ref this._fireEventsAgain);
            }

            if (!this._loggingAlreadyStarted)
            {
                this._logStream = new MemoryStream();
                this._logStreamWriter = new StreamWriter(this._logStream);

                this._htmlLogWriter = new HtmlLogWriterCS();
                this._htmlLogWriter.InitializeLogWriter(this._connections, this._events, SUBCOMPONENT, this._fireEventsAgain);
                this._htmlLogWriter.OpenLogHtml(this._logStreamWriter);

                this._loggingAlreadyStarted = true;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public override void CloseLog()
        {
            StreamReader logStreamReader;
            string logOutput;
            StreamWriter fileStreamWriter;

            string subComponentInfo = SUBCOMPONENT + "-CloseLog";

            if (this._fireEventsAgain)
            {
                this._events.FireInformation(0, subComponentInfo, "Closing HTML log.", String.Empty, 0, ref this._fireEventsAgain);
            }

            // The following IF test is necessary because
            //  OpenLog and CloseLog are called multiple times.

            if (this._packageHasEnded)
            {
                try
                {
                    // Get cached log output and clean up.
                    this._htmlLogWriter.CloseLogHtml();

                    this._logStreamWriter.Flush();
                    this._logStream.Position = 0;
                    logStreamReader = new StreamReader(this._logStream);
                    logOutput = logStreamReader.ReadToEnd();

                    this._logStreamWriter.Close();
                    logStreamReader.Close();

                    // Write cached log output to log file all at once.
                    fileStreamWriter = new StreamWriter(this._logFile);
                    {
                        fileStreamWriter.Write(logOutput);
                        fileStreamWriter.Close();
                    }
                }
                catch (Exception ex)
                {
                    this._events.FireError(0, subComponentInfo, ex.Message, String.Empty, 0);
                }
            }
        }

        #endregion

        #region Log: Write individual log entries

        public override void Log(string logEntryName, string computerName, string operatorName, string sourceName, string sourceID, string executionID, string messageText, DateTime startTime, DateTime endTime, int dataCode, byte[] dataBytes)
        {
            string subComponentInfo = SUBCOMPONENT + "-Log";

            if (this._fireEventsAgain)
            {
                this._events.FireInformation(0, subComponentInfo, "Writing HTML log entry for " + logEntryName, String.Empty, 0, ref this._fireEventsAgain);
            }

            this._htmlLogWriter.LogHtml(logEntryName, computerName, operatorName, sourceName, sourceID, executionID, messageText, startTime, endTime, dataCode, dataBytes);

            if (logEntryName == PACKAGE_END_EVENT)
            {
                this._packageHasEnded = true;
            }
        }

        #endregion
    }
}
